/***************************************************************************************************
  *Copyright(C),2010-2016,Sumscope
  *FileName	:  TableCodec_Encode.cpp
  *Author	:  scofined.qi
  *Version	:  1.0
  *Date		:  2019/09/11
  *Desc		:  编码
  *Relation :  
  *Others	:  //其他内容说明
  *Function :  //主要函数列表，每条记录应包含函数名及功能简要说明
  *History	:  //修改历史记录列表，每条修改记录应包含修改日期、修改者及修改内容简介
***************************************************************************************************/
#include "TableCodec.h"
#include "common_util.h"
#include "FieldCollector.h"
#include "StopCodec.h"
#include "BitViewWriter.h"
#include "BitView.h"

#include "../zstd.h"
#include <cstdlib>
namespace qb{
	namespace proto{
		int GetIntBitWidth(int number)
		{
			if (number < 0)
				return 0;
			for (int i = 0; i < 31; i++)
			{
				if (number < (1 << (i + 1)))
				{
					return i+1;
				}
			}
			return 0;
		}
		int CalcNamesSize(kEncodeType type,const CollectorArray& fields)
		{
			int sizeSum  = 0;
			for (int i = 0; i < (int)fields.size(); i++)
			{
				const FieldCollectorPtr& fc = fields[i];
				const FieldKey& key = fc->GetKey();
				VarValue val(key.c_str(), key.size());
				int size = StopCodec::CalcEncodeSize(type, val);
				sizeSum += size;
				if (size < 0)
					return -1;
			}
			return sizeSum;

		}
		class IncArrayBuilder{
			std::vector<IncArray>&	m_arrays;
			IncArray			m_current;
		public:
			IncArrayBuilder(std::vector<IncArray>& arrays) :m_arrays(arrays){}
			~IncArrayBuilder(){}
			void Finish(){
				if (m_current.count() > 0)
					m_arrays.push_back(m_current);
			}
			void Add(int num)
			{
				if (m_current.add(num))
					return;
				if (m_current.count() > 0)
					m_arrays.push_back(m_current);
				m_current.reset(num);
			}
		};
		kEncodeType TableCodec::DeduceNameType(kNameType& nametype,std::vector<IncArray>& arrays)
		{
			kEncodeType types[] = {
				kEncodeTypeStrAlpNum, kEncodeTypeStrNum, kEncodeTypeVarInt
			};
			nametype = kNameTypeString;
			kEncodeType type = kEncodeTypeStrRaw;
			int minSize = CalcNamesSize(type, m_collectors);
			bool isInt = false;
			for (int i = 0; i < _countof(types);i++)
			{
				int size = CalcNamesSize(types[i], m_collectors);
				if (size > 0 && size < minSize)
				{
					if (types[i] == kEncodeTypeVarInt)
						isInt = true;
					type = types[i];
					minSize = size;
				}
			}
			if (isInt)
			{
				// 如果字段名称全都是正整数,那么尝试用固定宽度bit位来编码,即kEncodeTypeFixedBit
				nametype = kNameTypeIntScatter;
				int lastkey = atoi(m_collectors[0]->GetKey().c_str());
				int smaller = 0, larger = 0, increment = 0;
				for (int i = 1; i < (int)m_collectors.size();i++)
				{
					int key = atoi(m_collectors[i]->GetKey().c_str());
					if (smaller > 0 && larger > 0)
						break;
					smaller += (lastkey < key) ? 1 : 0;
					larger += (lastkey > key) ? 1 : 0;
					increment += (key == lastkey + 1) ? 1 : 0;
					lastkey = key;
				}
				if (increment == m_collectors.size() - 1)
					nametype = kNameTypeIntIncrement;
				else if ((smaller > 0 && larger == 0) || (smaller==0 && larger>0))
					nametype = kNameTypeIntSortted;

				// 快速返回
				if (nametype == kNameTypeIntIncrement)
					return kEncodeTypeVarInt;

				// 依据键值构造多个等差数列
				arrays.clear();
				IncArrayBuilder builder(arrays);
				for (int i = 0; i < (int)m_collectors.size(); i++)
				{
					int key = atoi(m_collectors[i]->GetKey().c_str());
					builder.Add(key);
				}
				builder.Finish();
				int size = StopCodec::CalcEncodeSize(kEncodeTypeVarInt, VarValue((int)arrays.size()));
				for (int i = 0; i < (int)arrays.size();i++)
				{
					IncArray& ia = arrays[i];
					size += StopCodec::CalcEncodeSize(kEncodeTypeVarInt, VarValue(ia.base()));
					size += StopCodec::CalcEncodeSize(kEncodeTypeVarInt, VarValue(ia.delta()));
					size += StopCodec::CalcEncodeSize(kEncodeTypeVarInt, VarValue(ia.count()));
				}
				if (size < minSize)
				{
					nametype = kNameTypeIntMultiIncrement;
					return kEncodeTypeVarInt;
				}
			}
			return type;
		}
		namespace{
			bool IsStrInteger(const char* str, int len)
			{
				if (str == NULL || len <= 0)
					return false;
				for (int i = 0; i < len; i++)
				{
					unsigned char uc = str[i];
					if (str[i] < '0' || str[i] > '9')
						return false;
				}
				return true;
			}
			bool IsAllInteger(const CollectorArray& collectors)
			{
				for (int i = 0; i < (int)collectors.size(); i++)
				{
					const FieldKey& key = collectors[i]->GetKey();
					if (!IsStrInteger(key.c_str(), key.size()))
						return false;
				}
				return true;
			}
			int CollectorCompareByName(const void* v1, const void* v2)
			{
				FieldCollectorPtr c1 = *((FieldCollectorPtr*)v1);
				FieldCollectorPtr c2 = *((FieldCollectorPtr*)v2);
				return strcmp(c1->GetKey().c_str(), c2->GetKey().c_str());
			}
			int CollectorCompareByInt(const void* v1, const void* v2)
			{
				FieldCollectorPtr c1 = *((FieldCollectorPtr*)v1);
				FieldCollectorPtr c2 = *((FieldCollectorPtr*)v2);
				int i1 = atoi(c1->GetKey().c_str());
				int i2 = atoi(c2->GetKey().c_str());
				return i1 - i2;
			}
		}
		int TableCodec::Encode(unsigned char* buf, int capcity, bool& zipped)
		{
			// 将收集到的数据进行编码,并返回长度
			if (buf == NULL || capcity <= 0)
				return -1;

			for (int i = 0; i < (int)m_collectors.size(); i++)
			{
				BASSERT(m_collectors[i]->GetCount() == m_row);
			}

			// 调整字段顺序
			if (m_collectors.size() > 1)
			{
				if (IsAllInteger(m_collectors))
					std::qsort(&m_collectors.at(0), m_collectors.size(), sizeof(FieldCollectorPtr), CollectorCompareByInt);
				else
					std::qsort(&m_collectors.at(0), m_collectors.size(), sizeof(FieldCollectorPtr), CollectorCompareByName);
			}
			StopCodec encoder(buf, capcity,true);

			// 编码头部
			//BASSERTS(m_col == m_collectors.size(), "列:%d 实际上有%d列", m_col, m_collectors.size());
			unsigned char flag = kEncodeTypeTable;
			if (m_dicts.size() > 0)
				flag |= TableFlagHasDict;
			if (m_use_key)
				flag |= TableFlagHasFieldNames;

			int nlen=encoder.AddValue(kEncodeTypeVarInt, VarValue(flag));
			encoder.AddValue(kEncodeTypeVarInt, VarValue(m_row));
			encoder.AddValue(kEncodeTypeVarInt, VarValue((int)m_collectors.size()));
			if (flag & TableFlagHasDict)
			{
				encoder.AddValue(kEncodeTypeVarInt, VarValue((int)m_dicts.size()));
				if (m_dicts.size() > 0)
				{
					for (int i = 0; i < (int)m_dicts.size(); i++)
					{
						FieldCollectorPtr& fc = m_collectors[i];
						fc->Encode(&encoder, this);
					}
				}
			}

			if (flag & TableFlagHasFieldNames)
			{
				// 推断用哪种更省空间
				std::vector<IncArray> arrays;
				kNameType nametype = kNameTypeString;
				kEncodeType type = DeduceNameType(nametype, arrays);
				unsigned char flag = (nametype << TypeBitCount) | type;
				encoder.AddValue(kEncodeTypeVarInt, VarValue((int)flag));
				if (type == kEncodeTypeVarInt && nametype == kNameTypeIntIncrement)
				{
					int firstkey = atoi(m_collectors[0]->GetKey().c_str());
					encoder.AddValue(kEncodeTypeVarInt, VarValue(firstkey));
				}
				else if (type == kEncodeTypeVarInt && nametype == kNameTypeIntMultiIncrement)
				{
					encoder.AddValue(kEncodeTypeVarInt, VarValue((int)arrays.size()));
					for (int i = 0; i < (int)arrays.size(); i++)
					{
						IncArray& ia = arrays[i];
						encoder.AddValue(kEncodeTypeVarInt, VarValue(ia.base()));
						encoder.AddValue(kEncodeTypeVarInt, VarValue(ia.delta()));
						encoder.AddValue(kEncodeTypeVarInt, VarValue(ia.count()));
					}
				}
				else
				{
					for (int i = 0; i < (int)m_collectors.size(); i++)
					{
						FieldCollectorPtr& fc = m_collectors[i];
						const FieldKey& key = fc->GetKey();
						if (type == kEncodeTypeVarInt)
							encoder.AddValue(type, VarValue(atoi(key.c_str())));
						else
							encoder.AddValue(type, VarValue(key.c_str(), key.size()));
					}
				}
			}

			for (int i = 0; i < (int)m_collectors.size(); i++)
			{
				FieldCollectorPtr& fc = m_collectors[i];
				fc->Encode(&encoder,this);
			}

			// 对于小尺寸消息,取消
			int size = encoder.GetPos();
			PrepareBuffer(size);
	
			if (m_use_zip && m_zbuf && m_zbuf_size>size)
			{
				// 如果压缩后的尺寸比当前编码尺寸更小,那么就使用压缩
				size_t nzip = ZSTD_compress(m_zbuf, m_zbuf_size, buf + nlen, size - nlen, 20);
				int s1=StopCodec::CalcEncodeSize(kEncodeTypeVarInt, VarValue((int)nzip));
				int s2=StopCodec::CalcEncodeSize(kEncodeTypeVarInt, VarValue(size - nlen));
				if (!ZSTD_isError(nzip) && (int)(nzip+s1+s2) < size - nlen)
				{
					StopCodec enc(buf, capcity, true);
					flag |= TableFlagHasZipped;
					enc.AddValue(kEncodeTypeVarInt, VarValue(flag));
					enc.AddValue(kEncodeTypeVarInt, VarValue((int)nzip));
					enc.AddValue(kEncodeTypeVarInt, VarValue(size - nlen));
					size = enc.GetPos();
					memcpy(buf + size, m_zbuf, nzip);
					zipped = true;
					return size + nzip;
				}

			}
			return encoder.GetPos();
		}

	}//namespace base;
}//namespace ss;
